/***********************************************************************
	filename: 	CEGUIImage.cpp
	created:	13/3/2004
	author:		Paul D Turner
	
	purpose:	Implementation of Image class members
*************************************************************************/

#include "hgeImage.h"
#include "hgeExceptions.h"
#include "hgeTexture.h"
#include "hgeRenderer.h"
#include "hgeVertex.h"
#include "hgeGuiSystem.h"
#include "hgeGeometryBuffer.h"
//#include "hgePropertyHelper.h"
#include <cmath>
#include <iostream>

/*************************************************************************
	Constructor
*************************************************************************/
Image::Image(Texture& texture, const String& name, const hgeRect& area, const Point& render_offset, float horzScaling, float vertScaling) :
	d_texture(&texture),
	d_area(area),
	d_offset(render_offset),
	d_name(name)
{
	if (!d_texture)
	{
		throw NullObjectException("Image::Image - Texture object supplied for Imageset creation must be valid.");
	}

	// setup initial image scaling
	setHorzScaling(horzScaling);
	setVertScaling(vertScaling);
}

Image::Image(const String& filename, const String& name, const hgeRect& area, const Point& render_offset, float horzScaling, float vertScaling) :
d_area(area),
d_offset(render_offset),
d_name(name)
{
	d_texture =
		&System::getSingleton().getRenderer()->createTexture(filename);

	// setup initial image scaling
	setHorzScaling(horzScaling);
	setVertScaling(vertScaling);
}

Image::Image(const String& filename, const String& name) :
d_name(name)
{
	d_texture =
		&System::getSingleton().getRenderer()->createTexture(filename);
	
	Size sz = d_texture->getSize();
	d_area = hgeRect(0, 0, sz.d_x, sz.d_y);
	d_offset = Point(0, 0);

	setHorzScaling(1.0f);
	setVertScaling(1.0f);
}

/*************************************************************************
	Copy constructor
*************************************************************************/
Image::Image(const Image& image) :
	d_texture(image.d_texture),
	d_area(image.d_area),
	d_offset(image.d_offset),
	d_scaledWidth(image.d_scaledWidth),
	d_scaledHeight(image.d_scaledHeight),
	d_scaledOffset(image.d_scaledOffset),
	d_name(image.d_name)
{
}

/*************************************************************************
	destructor
*************************************************************************/
Image::~Image(void)
{
}


/*************************************************************************
	set the horizontal scaling factor to be applied to this Image
*************************************************************************/
void Image::setHorzScaling(float factor)
{
	d_scaledWidth		= PixelAligned(d_area.getWidth() * factor);
	d_scaledOffset.d_x	= PixelAligned(d_offset.d_x * factor);
}


/*************************************************************************
	set the vertical scaling factor to be applied to this Image
*************************************************************************/
void Image::setVertScaling(float factor)
{
	d_scaledHeight		= PixelAligned(d_area.getHeight() * factor);
	d_scaledOffset.d_y	= PixelAligned(d_offset.d_y * factor);
}


/*************************************************************************
    Clip and then queue the image to be rendered.
*************************************************************************/
void Image::draw(GeometryBuffer& buffer, const hgeRect& dest_rect,
    const hgeRect* clip_rect, const ColourRect& colours,
    QuadSplitMode quad_split_mode) const
{
    hgeRect source_rect(dest_rect);

    // apply rendering offset to the destination Rect
    source_rect.offset(d_scaledOffset);

	// get the rect area that we will actually draw to (i.e. perform clipping)
	hgeRect final_rect(clip_rect ? dest_rect.getIntersection(*clip_rect) : dest_rect );

	// check if rect was totally clipped
	if ((final_rect.getWidth() == 0) || (final_rect.getHeight() == 0))
		return;

	// Obtain correct scale values from the texture
	const float x_scale = d_texture->getTexelScaling().d_x;
	const float y_scale = d_texture->getTexelScaling().d_y;

	float tex_per_pix_x = source_rect.getWidth() / dest_rect.getWidth();
	float tex_per_pix_y = source_rect.getHeight() / dest_rect.getHeight();

	// calculate final, clipped, texture co-ordinates
	hgeRect  tex_rect((source_rect.d_left + ((final_rect.d_left - dest_rect.d_left) * tex_per_pix_x)) * x_scale,
		(source_rect.d_top + ((final_rect.d_top - dest_rect.d_top) * tex_per_pix_y)) * y_scale,
		(source_rect.d_right + ((final_rect.d_right - dest_rect.d_right) * tex_per_pix_x)) * x_scale,
		(source_rect.d_bottom + ((final_rect.d_bottom - dest_rect.d_bottom) * tex_per_pix_y)) * y_scale);

	final_rect.d_left	= PixelAligned(final_rect.d_left);
	final_rect.d_right	= PixelAligned(final_rect.d_right);
	final_rect.d_top	= PixelAligned(final_rect.d_top);
	final_rect.d_bottom	= PixelAligned(final_rect.d_bottom);

	Vertex vbuffer[6];

	// vertex 0
	vbuffer[0].position   = Vector3(final_rect.d_left, final_rect.d_top, 0.0f);
	vbuffer[0].colour_val = colours.d_top_left;
	vbuffer[0].tex_coords = Vector2(tex_rect.d_left, tex_rect.d_top);

	// vertex 1
	vbuffer[1].position   = Vector3(final_rect.d_left, final_rect.d_bottom, 0.0f);
	vbuffer[1].colour_val = colours.d_bottom_left;
	vbuffer[1].tex_coords = Vector2(tex_rect.d_left, tex_rect.d_bottom);

	// vertex 2
	vbuffer[2].position.d_x   = final_rect.d_right;
	vbuffer[2].position.d_z   = 0.0f;
	vbuffer[2].colour_val     = colours.d_bottom_right;
	vbuffer[2].tex_coords.d_x = tex_rect.d_right;

	// top-left to bottom-right diagonal
	if (quad_split_mode == TopLeftToBottomRight)
	{
		vbuffer[2].position.d_y   = final_rect.d_bottom;
		vbuffer[2].tex_coords.d_y = tex_rect.d_bottom;
	}
	// bottom-left to top-right diagonal
	else
	{
		vbuffer[2].position.d_y   = final_rect.d_top;
		vbuffer[2].tex_coords.d_y = tex_rect.d_top;
	}

	// vertex 3
	vbuffer[3].position   = Vector3(final_rect.d_right, final_rect.d_top, 0.0f);
	vbuffer[3].colour_val = colours.d_top_right;
	vbuffer[3].tex_coords = Vector2(tex_rect.d_right, tex_rect.d_top);

	// vertex 4
	vbuffer[4].position.d_x   = final_rect.d_left;
	vbuffer[4].position.d_z   = 0.0f;
	vbuffer[4].colour_val     = colours.d_top_left;
	vbuffer[4].tex_coords.d_x = tex_rect.d_left;

	// top-left to bottom-right diagonal
	if (quad_split_mode == TopLeftToBottomRight)
	{
		vbuffer[4].position.d_y   = final_rect.d_top;
		vbuffer[4].tex_coords.d_y = tex_rect.d_top;
	}
	// bottom-left to top-right diagonal
	else
	{
		vbuffer[4].position.d_y   = final_rect.d_bottom;
		vbuffer[4].tex_coords.d_y = tex_rect.d_bottom;
	}

	// vertex 5
	vbuffer[5].position = Vector3(final_rect.d_right, final_rect.d_bottom, 0.0f);
	vbuffer[5].colour_val= colours.d_bottom_right;
	vbuffer[5].tex_coords = Vector2(tex_rect.d_right, tex_rect.d_bottom);

	// TODO: Remove cast when GeometryBuffer gets it's APIs fixed!
	buffer.setActiveTexture((Texture*)d_texture);
	buffer.appendGeometry(vbuffer, 6);
}


/*************************************************************************
	String object containing the name of this Image	
*************************************************************************/
const String& Image::getName(void) const
{
	return d_name;
}

/*************************************************************************
	Return Rect describing the source texture area used by this Image.
*************************************************************************/
const hgeRect& Image::getSourceTextureArea(void) const
{
    return d_area;
}